import * as ExcelJS from 'exceljs';
import * as Images from 'images';
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import * as Mongoose from 'mongoose';
import { type Model } from 'mongoose';
import { type Product } from './product.mode';
import { ProductAddDataDTO } from './product.dto';

import * as utils from './utils';
import { chunk } from '@/utils';

interface ResponseData {
  sn: string;
  product: Product[];
  groupSize: number;
  sheetGroup: number;
}

/**
 * 红色分割线间隔
 */
const gap = 6;

const offset = {
  /** 列间隔 */
  size: 0,
  get x() {
    return this.size + 2;
  },
  columnA(colIndex: number) {
    return (colIndex + 1) * this.x - (this.size + 1);
  },
  columnB(colIndex: number) {
    return (colIndex + 1) * this.x - this.size;
  },
  split: 5,
  /** 最大列数 */
  wrap: 6,
};

@Injectable()
export class ProductService {
  constructor(
    @InjectModel('product') private readonly productModule: Model<Product>,
  ) {}

  public async getProducts() {
    const data = await this.productModule.find();

    return data.map((item) => item.toObject());
  }

  public async getProduct(id: string) {
    try {
      const data = await this.productModule.findById(id);
      if (data) return data.toObject();
      return null;
    } catch {
      throw new HttpException('找不到产品', HttpStatus.OK);
    }
  }

  public async insertProducts(data: ProductAddDataDTO[]) {
    const sn = new Mongoose.Types.ObjectId().toHexString();
    const dataMap: Record<string, ProductAddDataDTO[]> = Object.create(null);

    data.forEach((item) => {
      if (Object.prototype.hasOwnProperty.call(dataMap, item.store)) {
        dataMap[item.store].push(item);
        return;
      }

      dataMap[item.store] = [item];
    });

    const result = Object.values(dataMap).flatMap((value) => {
      const storeId = new Mongoose.Types.ObjectId().toHexString();
      return value.map((it) => ({ ...it, storeId }));
    });

    const insertData: Omit<Product, 'id' | 'storeId' | 'sn'>[] = result.map(
      (item) => ({ ...item, sn }),
    );

    const products = await this.productModule.insertMany(insertData);

    return { sn, data: products.map((item) => item.toObject()) };
  }

  public async exportExcel(data: ResponseData) {
    console.time('打散产品');
    const hashOrderData = utils.hashOrder(
      utils.createOrder(data.product),
      data.groupSize,
    );
    console.timeEnd('打散产品');

    const workbook = new ExcelJS.Workbook();
    workbook.created = new Date();

    /**
     * 产品图片 map
     * workbook Image id
     */
    const imageMap = await utils
      .downloadImage(data.product)
      .then((response) => {
        const result: Record<
          string,
          { image: string; productId: string; buffer: Buffer }
        > = Object.create(null);
        response.forEach((buffer, index) => {
          const buf = Images(buffer).resize(170).toBuffer('jpeg');
          result[data.product[index].id] = {
            buffer: buf,
            image: data.product[index].image,
            productId: data.product[index].id,
          };
        });

        return result;
      });

    const worksheetTable = chunk(hashOrderData, data.sheetGroup);

    worksheetTable.forEach((row, i) => {
      const worksheet = workbook.addWorksheet(`sheet${i + 1}`);

      /**
       * @description 分组换行时  Y轴偏移量 += 当前组数量
       *
       * Y轴偏移量
       */
      const wrapY = 0;

      /**
       * 分组换行时 动态X轴偏移量
       */
      const wrapX = 0;

      /**
       * 分组换行时 填充分隔行 Y轴偏移量 +1
       */
      let offsetGap = 0;

      const sheetData = utils.renderSheet(row);

      sheetData.forEach((group, rowIndex) => {
        group.forEach((item, colIndex) => {
          if ((rowIndex + 1 + offsetGap) % gap === 0) {
            const row = worksheet.addRow(Array(group.length * 2).fill('xxxx'));
            row.fill = {
              type: 'pattern',
              pattern: 'solid',
              fgColor: { argb: 'e1251b' },
            };

            offsetGap += 1;
          }

          if (!item) return;

          /** 产品图片column */
          const columnA = worksheet.getColumn(offset.columnA(colIndex));
          /** 产品描述column */
          const columnB = worksheet.getColumn(offset.columnB(colIndex));
          columnA.width = 24;
          columnB.width = 26;

          /**
           * 因为 getRow index 下标从 1 开始 故 + 1
           */
          const row = worksheet.getRow(rowIndex + 1 + offsetGap);
          const cell = row.getCell(offset.columnB(colIndex));
          row.height = 130;

          const img = workbook.addImage({
            buffer: imageMap[item.id].buffer,
            extension: 'jpeg',
          });

          /**
           * 因为 col 和 row 下标从 0 开始 故 - 1
           */
          worksheet.addImage(img, {
            tl: {
              col: offset.columnA(colIndex) - 1,
              row: rowIndex + offsetGap,
            },
            ext: { width: 170, height: 220 },
            editAs: 'oneCell',
          });

          cell.value = `店铺名：${item.store}\r关键词：${item.keyword}\rSKU：${item.sku}\r价格：${item.price}\r备注：${item.remark}`;

          cell.style.alignment = {
            horizontal: 'center',
            vertical: 'top',
            wrapText: true,
          };

          cell.font = {
            size: 14,
          };

          if (item.warn) {
            cell.style.font = {
              size: 14,
              color: { argb: 'e1251b' },
            };
          }

          // * 一组内价格超过1800 高亮背景 #FCD5B4
          if (item.alert) {
            cell.style.fill = {
              type: 'pattern',
              pattern: 'solid',
              fgColor: { argb: 'FCD5B4' },
            };
          }
        });
      });
    });

    return workbook.xlsx.writeBuffer();
  }
}
